using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;

namespace OllamaProxy
{

    public class Model
    {
        public string name;
        [JsonPropertyName("object")]
        public string _object="model";
        public long created;
        public string owned_by;

        public void calcOwner()
        {
            int idx=name.IndexOf("/");
            if(idx>=0) 
                owned_by=name.Substring(0,idx);
            else
                owned_by="library";
        }

    }

    public class ModelListResponse
    {
        public Model[] data;
        [JsonPropertyName("object")]
        public string _object="list";

        public ModelListResponse(JsonElement res)
        {
            if(res.ValueKind==JsonValueKind.Object)
            {
                var models = res.GetProperty("models");
                data=new Model[models.GetArrayLength()];
                int i=0;
                foreach(var model in models.EnumerateArray())
                {
                    data[i]=new Model();
                    data[i].name=model.GetProperty("model").GetString();
                    data[i].created=new DateTimeOffset(
                        model.GetProperty("modified_at").GetDateTime()).ToUnixTimeSeconds();
                    data[i].calcOwner();
                    i++;
                }
            }
        }
    }

    public class ImageUrl
    {
        public string url;
    }

    public class ChatCompeltionsMessageContent
    {
        public string type;
        public string text;
        public ImageUrl image_url;
    }

    public class ChatCompeltionsMessage {
        public string role;
        public JsonElement content;
    }

    public class OllamaChatCompeltionsMessage {
        public string role {get;set;}
        public string content {get;set;}
        public string[] images {get;set;}
    }


    public class ChatCompeltionsResponseMessage {
        public string role;
        public string content;
    }

    public class ChatCompeltionsChoices {
        public int index;
        public ChatCompeltionsResponseMessage delta;
        public string finish_reason;  // "done" or null
    }

    public class ChatCompeltionsStreamOption
    {
        public bool include_usage;
    }

    public class ChatCompeltionsRequest {
        public string model;
        
        public ChatCompeltionsMessage[] messages;
        public double frequency_penalty=0;  // -2.0 和 2.0
        public double presence_penalty=0;  // 默认为 0 -2.0 到 2.0 之间的数字
        public string response_format="";  // url or  b64_json
        public double seed=0;
        public string stop;
        public bool stream;
        public ChatCompeltionsStreamOption stream_options;  //包含一个include_usage属性

        public double temperature=0;
        
        public double top_p=0;
        public double max_tokens=0;
        
        public string[] tools;

        public ChatCompeltionsChoices choices;
    }

    public class OllamaChatCompeltionsRequest {
        public string model {get;set;}
        public OllamaChatCompeltionsMessage[] messages {get;set;}
        public string[] tools {get;set;}
        public bool stream {get;set;}
        public Dictionary<string,JsonElement> options {get;set;} //可以包含如下参数
            // "num_keep": 5,
            // "seed": 42,
            // "num_predict": 100,
            // "top_k": 20,
            // "top_p": 0.9,
            // "min_p": 0.0,
            // "typical_p": 0.7,
            // "repeat_last_n": 33,
            // "temperature": 0.8,
            // "repeat_penalty": 1.2,
            // "presence_penalty": 1.5,
            // "frequency_penalty": 1.0,
            // "mirostat": 1,
            // "mirostat_tau": 0.8,
            // "mirostat_eta": 0.6,
            // "penalize_newline": true,
            // "stop": ["\n", "user:"],
            // "numa": false,
            // "num_ctx": 1024,
            // "num_batch": 2,
            // "num_gpu": 1,
            // "main_gpu": 0,
            // "low_vram": false,
            // "vocab_only": false,
            // "use_mmap": true,
            // "use_mlock": false,
            // "num_thread": 8

        public OllamaChatCompeltionsRequest(JsonElement req)
        {
            foreach(var p in req.EnumerateObject())
            {
                if(p.Name=="model")
                {
                    this.model=p.Value.GetString();
                }
                else if(p.Name=="stream")
                {
                    this.stream=p.Value.GetBoolean();
                }
                else if(p.Name=="tools"&&p.Value.ValueKind==JsonValueKind.Array)
                {
                    this.tools=new string[p.Value.GetArrayLength()];
                    int i=0;
                    foreach(var m in p.Value.EnumerateArray())
                        this.tools[i++]=m.GetString();
                }
                else if(p.Name=="messages"&&p.Value.ValueKind==JsonValueKind.Array)
                {
                    this.messages=new OllamaChatCompeltionsMessage[p.Value.GetArrayLength()];
                    int i=0;
                    foreach(var m in p.Value.EnumerateArray())
                    {
                        messages[i]=new OllamaChatCompeltionsMessage();
                        messages[i].role=m.GetProperty("role").GetString();
                        var content=m.GetProperty("content");
                        if(content.ValueKind==JsonValueKind.String)
                        {
                            messages[i].content=content.GetString();
                        }
                        else if(content.ValueKind==JsonValueKind.Array)
                        {
                            messages[i].content=null;
                            List<string> images=new List<string>();
                            foreach(var e in content.EnumerateArray())
                            {
                                if(e.GetProperty("type").GetString()=="text")
                                {
                                    if(messages[i].content==null) 
                                        messages[i].content=e.GetProperty("text").GetString();
                                    else
                                        messages[i].content="\n"+e.GetProperty("text").GetString();
                                }
                                if(e.GetProperty("type").GetString()=="image_url")
                                {
                                    images.Add(e.GetProperty("image_url").GetProperty("url").GetString());
                                }                        
                            }
                            if(images.Count>0) messages[i].images=images.ToArray();
                        }
                        i++;
                    }
                }
                else 
                {
                    if(options==null) options=new Dictionary<string, JsonElement>();
                    options.Add(p.Name,p.Value);
                }
            }

        }

    }

    public class ChatCompeltionsStreamResponse {
        public string id;  // chatcmpl-448
        [JsonPropertyName("object")]
        public string _object="chat.completion.chunk";
        public string model;
        public long created;
        public string system_fingerprint="fp_ollama";
        public ChatCompeltionsChoices[] choices;
    }

    public class ChatCompeltionsResponseUsage {
        public int prompt_tokens;
        public int completion_tokens;
        public int total_tokens;
    }

    public class ChatCompeltionsResponse {
        public string id;  // chatcmpl-448
        [JsonPropertyName("object")]
        public string _object="chat.completion";
        public string model;
        public long created;
        public string system_fingerprint="fp_ollama";
        public ChatCompeltionsChoices[] choices;
        public ChatCompeltionsResponseUsage usage;
    }

    public class ErrorResponse
    {
        public Dictionary<string,object> error=new Dictionary<string,object>();

        public static ErrorResponse create(string key,object value)
        {
            ErrorResponse res=new ErrorResponse();
            res.error.Add(key,value);
            return res;
        }

        public static ErrorResponse create(string message)
        {
            ErrorResponse res=new ErrorResponse();
            res.error.Add("message", message);
            res.error.Add("type", "api_error");
            return res;
        }

        public ErrorResponse Add(string key,object value) {
            error.Add(key,value);
            return this;
        }
    }

    public class OllamaConfig
    {
        public string base_url="http://127.0.0.1:11434";
    }

    [JsonSerializable(typeof(Dictionary<string,JsonElement>))]
    [JsonSerializable(typeof(ImageUrl))]
    [JsonSerializable(typeof(ChatCompeltionsMessage))]
    [JsonSerializable(typeof(OllamaChatCompeltionsMessage))]
    [JsonSerializable(typeof(ChatCompeltionsMessageContent))]
    [JsonSerializable(typeof(ChatCompeltionsChoices))]
    [JsonSerializable(typeof(ChatCompeltionsStreamResponse))]
    [JsonSerializable(typeof(ChatCompeltionsResponseUsage))]
    [JsonSerializable(typeof(ChatCompeltionsStreamOption))]
    [JsonSerializable(typeof(ChatCompeltionsRequest))]
    [JsonSerializable(typeof(OllamaChatCompeltionsRequest))]
    [JsonSerializable(typeof(ChatCompeltionsResponse))]
    [JsonSerializable(typeof(JsonElement))]
    [JsonSerializable(typeof(OllamaConfig))]
    [JsonSerializable(typeof(Model))]
    [JsonSerializable(typeof(ModelListResponse))]
    [JsonSerializable(typeof(ErrorResponse))]
    [JsonSerializable(typeof(Dictionary<string,object>))]
    [JsonSerializable(typeof(JsonElement))]
    [JsonSerializable(typeof(JsonNode))]
    [JsonSerializable(typeof(JsonArray))]
    [JsonSerializable(typeof(JsonObject))]
    [JsonSerializable(typeof(JsonValue))]
    [JsonSerializable(typeof(ProblemDetails))]
    [JsonSerializable(typeof(object))]
    internal partial class AppJsonSerializerContext : JsonSerializerContext
    {
    }
}